#include "stdafx.h"
#include "Spread_Business.h"
#include "NorthBridge/NorthBridge.h"
#include "Bios/User.h"
#include "Bus/DataBus.h"
#include "Register/ACC.h"
#include "Register/VCR.h"
#include "Register/GPR.h"

using namespace Spread_StatusMachine;


class SpreadBusiness::Spread_StatusMachine
{
public:
	Spread_StatusMachine(StatusMachineT* machine):m_machine(machine) {}
	~Spread_StatusMachine() { delete m_machine; }
	void OnEvent(Event const& event) { m_machine->OnEvent(event); } 
	char const* GetCurrentStatusName() const { return m_machine->GetCurrentStatus()->GetName().c_str(); }
private:
	StatusMachineT* m_machine;
};

SpreadBusiness::SpreadBusiness(User& user, SpreadArgument const& argument, MarketSnapshot const& marketsnapshot,
	VectorRegister& vectorRegister, DealingCache& dealingCache, OrderActionCache& actionCache,
	Exchange::FLAG minorExchangeConnection, Exchange::FLAG majorExchangeConnection)
:AutoUpdateUIBusiness(user)
,m_user(user),m_argument(argument),m_marketSnapshot(marketsnapshot),m_vectorRegister(vectorRegister)
,m_dealingCache(dealingCache),m_actionCache(actionCache),m_minorConnectionExchange(minorExchangeConnection),m_majorConnectionExchange(majorExchangeConnection)
,m_bullishLegs(ArbitrageLegs::CreateBullish(marketsnapshot,m_argument.GetContracts(),HedgeFlag::SPECULATE))
,m_bearishLegs(ArbitrageLegs::CreateBearish(marketsnapshot,m_argument.GetContracts(),HedgeFlag::SPECULATE))
,m_currentLegsPtr(NULL),m_position(argument.GetContracts()),m_profit(0.00),m_minorOpenQuantity(0),m_cancellationCounter(0)
{
	m_spreadMachine.reset(new Spread_StatusMachine(CreateMachine(*this)));
	AddContract(GetMajorIndex());
	AddContract(GetMinorIndex());
	m_bullishLegs.UpdateBind(m_argument.GetBullishBind());
	m_bearishLegs.UpdateBind(m_argument.GetBearishBind());

	if ((SpreadArgument::OFFSET==argument.GetOperation()) || (SpreadArgument::OFFSET_TODAY==argument.GetOperation()))
	{
		Market const& majorMarket = m_marketSnapshot.GetMarket(argument.GetContracts().GetMajor());
		Market const& minorMarket = m_marketSnapshot.GetMarket(argument.GetContracts().GetMinor());
		m_position.Update(argument.GetContracts().GetMajor(), Operation::OPEN, PositionValue(majorMarket.GetLastPrice(),argument.GetQuantity()));
		m_position.Update(argument.GetContracts().GetMinor(), Operation::OPEN, PositionValue(minorMarket.GetLastPrice(),argument.GetQuantity()));
	}
}

SpreadBusiness::~SpreadBusiness()
{
}

SpreadBusiness::Snapshot SpreadBusiness::GetSnapshot()
{
	if (NULL != m_currentLegsPtr)
	{
		int fartherDirection = m_currentLegsPtr->GetFartherOpenLeg().GetDirection();
		double openSpread = m_position.GetFarther().GetPrice() - m_position.GetNearby().GetPrice();
		if (SpreadArgument::OFFSET==m_argument.GetOperation() || SpreadArgument::OFFSET_TODAY==m_argument.GetOperation())
		{
			openSpread = IsBullOperation() ? m_argument.GetBullishBind().GetOpenSpread() : m_argument.GetBearishBind().GetOpenSpread();
		}
		return SpreadBusinessSnapshot(m_spreadMachine->GetCurrentStatusName(),fartherDirection,m_position.GetFarther().GetQuantity(),m_position.GetNearby().GetQuantity(),openSpread,m_profit,GetCancellationCounter());
	}
	else
	{
		return SpreadBusinessSnapshot(m_spreadMachine->GetCurrentStatusName(), 0, 0, 0, 0.00, 0.00, 0);
	}
}
void SpreadBusiness::UpdateUI(Snapshot const& snapshot)
{
	ChipMessage message = {0};
	message.type = ChipMessage::BUSINESS_COMMAND;
	BusinessCommand& cmd = *(BusinessCommand*)message.data;
	cmd.type = BusinessCommand::UI_UPDATE_BUSINESS_INFO;
	cmd.businessPtr = this;
	Snapshot& snap = *(Snapshot*)cmd.data;
	snap = snapshot;
	m_vectorRegister.GetInterfaceQueue().Push(message);
}

void SpreadBusiness::Init()
{
	m_currentLegsPtr = NULL;
	m_minorOpenHistory.Clear();
	m_majorOpenHistory.Clear();
	m_minorOffsetHistory.Clear();
	m_majorOffsetHistory.Clear();
	m_profit = 0.00;
	m_minorOpenQuantity = 0;
	if (!IsOffsetOperation() && !IsOffsetTodayOperation())
	{
		m_position.Clear();
	}
}
void SpreadBusiness::OnMinorMarket()
{
	OnMarket(m_marketSnapshot.GetMarket(GetMinorIndex()));
}
void SpreadBusiness::OnMajorMarket()
{
	OnMarket(m_marketSnapshot.GetMarket(GetMajorIndex()));
}
void SpreadBusiness::IncreaseUserPositionManually()
{
	Market const& nearbyMarket = m_marketSnapshot.GetMarket(GetNearbyIndex());
	PositionKey nearbyKey = GetPositionKey(nearbyMarket);
	PositionValue nearbyValue(nearbyMarket.GetLastPrice(), m_argument.GetQuantity());
	m_user.IncreasePosition(nearbyKey, nearbyValue);

	Market const& fartherMarket = m_marketSnapshot.GetMarket(GetFatherIndex());
	PositionKey fartherKey = GetPositionKey(fartherMarket);
	PositionValue fartherValue(fartherMarket.GetLastPrice(), m_argument.GetQuantity());
	m_user.IncreasePosition(fartherKey, fartherValue);
}
void SpreadBusiness::RestoreUserPositionManually()
{
	Market const& nearbyMarket = m_marketSnapshot.GetMarket(GetNearbyIndex());
	PositionKey nearbyKey = GetPositionKey(nearbyMarket);
	PositionValue const& nearbyValue = m_user.GetPosition(nearbyKey);
	if (nearbyValue.GetQuantity()>0)
	{
		m_user.RestorePosition(nearbyKey, m_argument.GetQuantity());
	}

	Market const& fartherMarket = m_marketSnapshot.GetMarket(GetFatherIndex());
	PositionKey fartherKey = GetPositionKey(fartherMarket);
	PositionValue const& fartherValue = m_user.GetPosition(fartherKey);
	if (fartherValue.GetQuantity()>0)
	{
		m_user.RestorePosition(fartherKey, m_argument.GetQuantity());
	}
}
void SpreadBusiness::LogCurrentMarket() const
{
	Market const& minorMarket = m_marketSnapshot.GetMarket(GetMinorIndex());
	Market const& majorMarket = m_marketSnapshot.GetMarket(GetMajorIndex());
	Log::Debug(FT("Business[%d] CurrentMajorMarket[%.2f, %.2f] CurrentMinorMarket[%.2f, %.2f]",GetId(),majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
}

bool SpreadBusiness::TryOpponentOpen()
{
	if (!IsOperationForbidden())
	{
		if (m_bullishLegs.CanOpponentOpen() && HaveEnoughFunds(m_bullishLegs, m_minorOpenQuantity))
		{
			m_currentLegsPtr = &m_bullishLegs;
			Log::Info(FT("Business[%d] Bullish O_Open Spread[%.2f] Quantity[%d]", GetId(), m_bullishLegs.GetOpponentOpenSpread(), m_minorOpenQuantity));
			return true;
		}

		if (m_bearishLegs.CanOpponentOpen() && HaveEnoughFunds(m_bearishLegs, m_minorOpenQuantity))
		{
			m_currentLegsPtr = &m_bearishLegs;
			Log::Info(FT("Business[%d] Bearish O_Open Spread[%.2f] Quantity[%d]", GetId(), m_bearishLegs.GetOpponentOpenSpread(), m_minorOpenQuantity));
			return true;
		}
	}
	return false;
}
bool SpreadBusiness::TryOpponentOffset()
{
	if (!IsOperationForbidden())
	{
		if (IsOpponentBullOffset() || IsOpponentBullOffsetToday())
		{
			m_currentLegsPtr = &m_bullishLegs;
		}
		if (IsOpponentBearOffset() || IsOpponentBearOffsetToday())
		{
			m_currentLegsPtr = &m_bearishLegs;
		}
		return IsOpponentOffsetMarket();
	}
	return false;
}
bool SpreadBusiness::TryLimitOpen()
{
	if (!IsOperationForbidden())
	{
		return IsEnterTheOpenRange() && HaveEnoughFunds(*m_currentLegsPtr,m_minorOpenQuantity);
	}
	return false;
}
bool SpreadBusiness::TryLimitOffset()
{
	if (!IsOperationForbidden())
	{
		if (IsLimitBullOffset() || IsLimitBullOffsetToday())
		{
			m_currentLegsPtr = &m_bullishLegs;
		}

		if (IsLimitBearOffset() || IsLimitBearOffsetToday())
		{
			m_currentLegsPtr = &m_bearishLegs;
		}
		return IsEnterTheOffsetRange();
	}
	return false;
}
bool SpreadBusiness::CanLimitOffset()
{
	if (!IsOperationForbidden())
	{
		return IsEnterTheOffsetRange();
	}
	return false;
}

bool SpreadBusiness::IsOpenMajorBetter(Dealing const& openDealing) const
{
	Market const& minorMarket = m_marketSnapshot.GetMarket(GetMinorIndex());
	Market const& majorMarket = m_marketSnapshot.GetMarket(GetMajorIndex());
	
	double dealPrice = openDealing.GetDealPrice();
	double expectPrice = m_currentLegsPtr->GetMajorOpenLeg().GetOpponentPrice();
	int sign = openDealing.IsBuy() ? 1 : -1;
	double majorOpenPrice = openDealing.IsBuy() ? majorMarket.GetBuy1().GetPrice() : majorMarket.GetSell1().GetPrice();
	double minorOffsetPrice = openDealing.IsBuy() ? minorMarket.GetBuy1().GetPrice() : minorMarket.GetSell1().GetPrice();
	
	double minorFee = minorMarket.CalculateFee(minorOffsetPrice, openDealing.GetDealQuantity(), m_currentLegsPtr->GetMinorOffsetLeg().GetOperation());
	double minorGain = sign*(minorOffsetPrice-dealPrice) * minorMarket.GetVariety().GetMultiplier();
	double majorGain = sign*(majorOpenPrice-expectPrice) * majorMarket.GetVariety().GetMultiplier();

	if (DoubleComparer::greater(majorGain, minorGain-minorFee))
	{
		Log::Info(FT("Business[%d] OpenMajorBetter: [OpenGain(%.2f)>OffsetGain(%.2f)-OffsetFee(%.2f)] minorDealPrice[%.2f] majorOpenPrice[%.2f] minorOffsetPrice[%.2f]",GetId(),majorGain,minorGain,minorFee,dealPrice,majorOpenPrice,minorOffsetPrice));
		return true;
	}
	Log::Info(FT("Business[%d] OffsetMinorBetter: [OpenGain(%.2f)<=OffsetGain(%.2f)-OffsetFee(%.2f)] minorDealPrice[%.2f] majorOpenPrice[%.2f] minorOffsetPrice[%.2f]",GetId(),majorGain,minorGain,minorFee,dealPrice,majorOpenPrice,minorOffsetPrice));
	return false;
}
bool SpreadBusiness::IsStillOpenMajor()
{
	if (IsOpponentOpen() && GetCurrrentLegs().CanOpponentOpen(m_minorOpenHistory.Last()))
	{
		return true;
	}
	else if (IsLimitOpen() && (true==WithinTheOpenRange()))
	{
		return true;
	}
	return IsOpenMajorBetter(m_minorOpenHistory.Last());
}
bool SpreadBusiness::IsLostLimitOpenCondition()
{
	return (false==WithinTheOpenRange()) || WithinThePriceRange(GetCurrrentLegs().GetMinorOpenLeg().IsBuy(),m_minorOpenHistory.Last().GetOrderPrice(),GetArgument().GetOverTick());
}
bool SpreadBusiness::IsLostLimitOffsetSpread()
{
	return (false==WithinTheOffsetRange()) || WithinThePriceRange(GetCurrrentLegs().GetMinorOffsetLeg().IsBuy(),m_minorOffsetHistory.Last().GetOrderPrice(),GetArgument().GetOverTick());
}

void SpreadBusiness::OpenMinor(OrderCondition::TYPE orderType)
{
	if (IsOpponentOpen())
	{
		double price = GetCurrrentLegs().GetMinorOpenLeg().GetOpponentPrice();
		Dealing& dealing = PushOrder(CreateOrder(GetCurrrentLegs().GetMinorOpenLeg(),m_minorOpenQuantity,price,orderType), m_minorConnectionExchange);
		m_minorOpenHistory.Add(dealing);
	}

	if (IsLimitOpen())
	{
		Leg const& minorOpenLeg = GetCurrrentLegs().GetMinorOpenLeg();
		double orderPrice = IsLimitBullOpen() ? CalculateMinorOrderPrice(minorOpenLeg,false) : CalculateMinorOrderPrice(minorOpenLeg,true);
		Dealing& dealing = PushOrder(CreateOrder(minorOpenLeg,m_minorOpenQuantity,orderPrice,orderType), m_minorConnectionExchange);
		m_minorOpenHistory.Add(dealing);
	}
}
void SpreadBusiness::CancelMinorOpen()
{
	if (!m_minorOpenHistory.IsEmpty())
	{
		Dealing const& openDealing = m_minorOpenHistory.Last();
		OrderAction action(openDealing.GetBusiness(),openDealing.GetClientId(),openDealing.GetExchange(),openDealing.GetSystemNo(),openDealing.GetContractCode(),openDealing.GetContractIndex(),openDealing.GetLocalId(),LocalIdACC::Generate(),openDealing.GetBusinessId(),openDealing.GetOperation());
		PushAction(action, m_minorConnectionExchange);
	}
}
void SpreadBusiness::OpenMajor()
{
	int quantity = m_position.GetMinor().GetQuantity() - m_position.GetMajor().GetQuantity();
	double price = GetCurrrentLegs().GetMajorOpenLeg().GetOpponentPrice(GetArgument().GetGivenTick());
	Dealing& dealing = PushOrder(CreateOrder(GetCurrrentLegs().GetMajorOpenLeg(),quantity,price,OrderCondition::FAK), m_majorConnectionExchange);
	m_majorOpenHistory.Add(dealing);
}
void SpreadBusiness::OffsetMinor(OrderCondition::TYPE orderType)
{
	if (IsOpponentCondition())
	{
		int quantity = m_position.GetMinor().GetQuantity() - m_position.GetMajor().GetQuantity();
		if (0==quantity)
		{
			quantity = min(GetCurrrentLegs().GetOpponentOffsetQuantity(),m_position.GetMinor().GetQuantity());
		}
		double orderPrice = GetCurrrentLegs().GetMinorOffsetLeg().GetOpponentPrice();
		Dealing& dealing = PushOrder(CreateOrder(GetCurrrentLegs().GetMinorOffsetLeg(),quantity,orderPrice,orderType), m_minorConnectionExchange);
		m_minorOffsetHistory.Add(dealing);
	}

	if (IsLimitCondition())
	{
		Leg const& minorOffsetLeg = GetCurrrentLegs().GetMinorOffsetLeg();
		double price = IsLimitBull() ? CalculateMinorOrderPrice(minorOffsetLeg,false) : CalculateMinorOrderPrice(minorOffsetLeg,true);
		Dealing& dealing = PushOrder(CreateOrder(minorOffsetLeg,m_position.GetMinor().GetQuantity(),price,orderType), m_minorConnectionExchange);
		m_minorOffsetHistory.Add(dealing);
	}
}
void SpreadBusiness::CancelMinorOffset()
{
	if (!m_minorOffsetHistory.IsEmpty())
	{
		Dealing const& offsetDealing = m_minorOffsetHistory.Last();
		OrderAction action(offsetDealing.GetBusiness(),offsetDealing.GetClientId(),offsetDealing.GetExchange(),offsetDealing.GetSystemNo(),offsetDealing.GetContractCode(),offsetDealing.GetContractIndex(),offsetDealing.GetLocalId(),LocalIdACC::Generate(),offsetDealing.GetBusinessId(),offsetDealing.GetOperation());
		PushAction(action, m_minorConnectionExchange);
	}
}
void SpreadBusiness::OffsetMajor()
{
	double price = GetCurrrentLegs().GetMajorOffsetLeg().GetLimitPrice();
	if (IsOpponentCondition())
	{
		Dealing& dealing = PushOrder(CreateOrder(GetCurrrentLegs().GetMajorOffsetLeg(),m_minorOffsetHistory.Last().GetDealQuantity(),price,OrderCondition::FAK), m_majorConnectionExchange);
		m_majorOffsetHistory.Add(dealing);
	}

	if (IsLimitCondition())
	{		
		int quantity = m_position.GetMajor().GetQuantity() - m_position.GetMinor().GetQuantity();
		Dealing& dealing = PushOrder(CreateOrder(GetCurrrentLegs().GetMajorOffsetLeg(),quantity,price,OrderCondition::FAK), m_majorConnectionExchange);
		m_majorOffsetHistory.Add(dealing);
	}
}
void SpreadBusiness::ClearAll()
{
	if (IsLimitCondition())
	{
		CancelMinorOffset();
	}

	int quantity = m_position.GetMinor().GetQuantity();
	double price = GetCurrrentLegs().GetMinorOffsetLeg().GetLimitPrice();
	Dealing& minorDealing = PushOrder(CreateOrder(GetCurrrentLegs().GetMinorOffsetLeg(),quantity,price,OrderCondition::FAK), m_minorConnectionExchange);
	m_minorOffsetHistory.Add(minorDealing);

	quantity = m_position.GetMajor().GetQuantity();
	price = GetCurrrentLegs().GetMajorOffsetLeg().GetLimitPrice();
	Dealing& majorDealing = PushOrder(CreateOrder(GetCurrrentLegs().GetMajorOffsetLeg(),quantity,price,OrderCondition::FAK), m_majorConnectionExchange);
	m_majorOffsetHistory.Add(majorDealing);
}

void SpreadBusiness::DoOnInit()
{	
	FT formatter("SpreadBusiness[%d] Contracts[%s|%s] Operation[%d] Condition[%d] Quantity[%d] OverTick[%d] GivenTick[%d] BullBand[%.2f|(%.2f,%.2f)] BearBand[(%.2f,%.2f)|%.2f] is added"
		,GetId(),GetMajorCode(),GetMinorCode(),GetArgument().GetOperation(),GetArgument().GetCondition(),GetArgument().GetQuantity(),GetArgument().GetOverTick(),GetArgument().GetGivenTick()
		,GetArgument().GetBullishBind().GetStopSpread(),GetArgument().GetBullishBind().GetOpenSpread(),GetArgument().GetBullishBind().GetOffsetSpread()
		,GetArgument().GetBearishBind().GetOpenSpread(),GetArgument().GetBearishBind().GetOffsetSpread(),GetArgument().GetBearishBind().GetStopSpread());
	Log::Info(formatter);
}
void SpreadBusiness::DoOnMarket(Market const& market)
{
	SnapshotNotifier notifier(*this);
	m_spreadMachine->OnEvent(Event(market));
	if (!IsZeroPosition() && !IsOffsetOperation() && !IsOffsetTodayOperation())
	{
		CalcProfit();
	}
}
void SpreadBusiness::DoOnPending(PendingItem const& pendingItem)
{
	UNREFERENCED_PARAMETER(pendingItem);
}
void SpreadBusiness::DoOnCommand(BusinessCommand const& command)
{
	SnapshotNotifier notifier(*this);
	switch (command.type)
	{
	case BusinessCommand::START:
		{
			Event event(Event::START_COMMAND);
			m_spreadMachine->OnEvent(event);
		}
		break;
	case BusinessCommand::STOP:
		{
			Event event(Event::STOP_COMMAND);
			m_spreadMachine->OnEvent(event);
		}
		break;
	case BusinessCommand::CLEAR:
		{
			Event event(Event::CLEAR_COMMAND);
			m_spreadMachine->OnEvent(event);
		}
		break;
	case BusinessCommand::EDIT:
		{
			Event event(Event::EDIT_COMMAND);
			m_spreadMachine->OnEvent(event);
		}
		break;
	case BusinessCommand::UPDATE_ARGUMENT:
		{
			SpreadArgument& argument= *(SpreadArgument*)command.data;
			UpdateArgument(argument);
		}
		break;
	default:
		break;
	}
}
void SpreadBusiness::DoOnTimeout(Timeout const& timeout)
{
	UNREFERENCED_PARAMETER(timeout);
}
void SpreadBusiness::DoOnDealing(Dealing const& dealing)
{
	SnapshotNotifier notifier(*this);
	m_spreadMachine->OnEvent(Event(dealing));
}
void SpreadBusiness::DoOnTrade(Trade const& trade)
{
	SnapshotNotifier notifier(*this);
	Dealing const& dealing = trade.GetDealing();
	m_position.Update(dealing.GetContractIndex(), dealing.GetOperation(), PositionValue(trade.GetDealPrice(),trade.GetDealQuantity()));	
}
void SpreadBusiness::DoOnExchangeStatus(int exchangeStatus)
{
	UNREFERENCED_PARAMETER(exchangeStatus);
}

PositionKey SpreadBusiness::GetPositionKey(Market const& market)
{
	Operation::TYPE operation = Operation::OFFSET;
	if (Exchange::SHFE == market.GetVariety().GetExchange())
	{
		operation = IsOffsetOperation() ? Operation::OFFSET : Operation::OFFSET_TODAY;
	}
	Direction::TYPE direction = IsBullOperation() ? Direction::BUY : Direction::SELL;
	return PositionKey(market.GetContractIndex(), direction, operation);
}
void SpreadBusiness::CalcProfit()
{
	double openSpread = m_position.GetFarther().GetPrice() - m_position.GetNearby().GetPrice();
	m_profit = GetCurrrentLegs().GetProfit(openSpread, m_position.GetMajor().GetQuantity());
}
void SpreadBusiness::UpdateArgument(Argument const& argument)
{
	m_argument = argument;
	m_bullishLegs.UpdateBind(argument.GetBullishBind());
	m_bearishLegs.UpdateBind(argument.GetBearishBind());
}
bool SpreadBusiness::HaveEnoughFunds(ArbitrageLegs const& legs, int& actuallyOpenQuantity)
{
	int tryQuantity = IsLimitOpen() ? (min(m_argument.GetQuantity(),legs.GetMajorOpenLeg().GetOpponentQuantity())) : (min(m_argument.GetQuantity(),legs.GetOpponentOpenQuantity()));
	for (int quantity=tryQuantity; quantity>0; --quantity)
	{
		//Just try to freeze in order to get actuallyOpenQuantity, not freeze actually.
		if (m_user.GetFunds().HaveEnoughCapital(legs.GetOpenTotalFreeze(quantity)))
		{
			actuallyOpenQuantity = quantity;
			return true;
		}
	}
	Log::Warn(FT("There is not enough funds[%.2f] to be used[%.2f]", m_user.GetFunds().GetAvailable(), legs.GetOpenTotalFreeze(1)));
	return false;
}

double SpreadBusiness::CalculateCurrentLimitSpread(bool isBearish, bool isOpen) const
{
	Market const& minorMarket = m_marketSnapshot.GetMarket(GetMinorIndex());
	Market const& majorMarket = m_marketSnapshot.GetMarket(GetMajorIndex());

	if ((isBearish==true && isOpen==true) || (isBearish==false && isOpen==false))
	{
		return m_argument.GetContracts().IsMajorEqualFarther() ? (majorMarket.GetSell1().GetPrice()-minorMarket.GetSell1().GetPrice()) : (minorMarket.GetBuy1().GetPrice()-majorMarket.GetBuy1().GetPrice());
	}
	else
	{
		return m_argument.GetContracts().IsMajorEqualFarther() ? (majorMarket.GetBuy1().GetPrice()-minorMarket.GetBuy1().GetPrice()) : (minorMarket.GetSell1().GetPrice()-majorMarket.GetSell1().GetPrice());
	}
}
bool SpreadBusiness::IsEnterTheOpenRange()
{
	Market const& majorMarket = m_marketSnapshot.GetMarket(GetMajorIndex());
	Market const& minorMarket = m_marketSnapshot.GetMarket(GetMinorIndex());
	if (majorMarket.IsContractValid())
	{
		if (DoubleComparer::less_equal(majorMarket.GetLastPrice(),0.00) || DoubleComparer::greater_equal(majorMarket.GetLastPrice(),DBL_MAX))
		{
			Log::Warn(FT("Business[%d] Get invalid majorMarket[%s:%.2f]", GetId(), GetMajorCode(), majorMarket.GetLastPrice()));
			return false;	
		}

		double rangeSpread = majorMarket.GetVariety().GetUnitPrice();
		if (IsLimitBullOpen())
		{
			m_currentLegsPtr = &m_bullishLegs;			
			double targetSpread = m_argument.GetBullishBind().GetOpenSpread();
			double currentSpread = CalculateCurrentLimitSpread(false, true);
			if (DoubleComparer::greater_equal(currentSpread+rangeSpread, targetSpread))
			{
				Log::Info(FT("Business[%d] Bullish L_Open Enter the open range: CurrentSpread[%.2f]+Range[%.2f] >= TargetSpread[%.2f] MajorMarket[%.2f %.2f] MinorMarket[%.2f %.2f]"
					,GetId(),currentSpread,rangeSpread,targetSpread,majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
				return true;
			}
		}
		if (IsLimitBearOpen())
		{
			m_currentLegsPtr = &m_bearishLegs;
			double targetSpread = m_argument.GetBearishBind().GetOpenSpread();
			double currentSpread = CalculateCurrentLimitSpread(true, true);
			if (DoubleComparer::less_equal(currentSpread-rangeSpread, targetSpread))
			{
				Log::Info(FT("Business[%d] Bearish L_Open Enter the open range: CurrentSpread[%.2f]-Range[%.2f] <= TargetSpread[%.2f] MajorMarket[%.2f %.2f] MinorMarket[%.2f %.2f]"
					,GetId(),currentSpread,rangeSpread,targetSpread,majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
				return true;
			}
		}
		m_currentLegsPtr = NULL;
	}
	else
	{
		Log::Error(FT("Business[%d] Get invalid contractIndex[%d]", GetId(), GetMajorIndex()));
		return false;
	}
	return false;
}
bool SpreadBusiness::WithinTheOpenRange() const
{
	Market const& majorMarket = m_marketSnapshot.GetMarket(GetMajorIndex());
	Market const& minorMarket = m_marketSnapshot.GetMarket(GetMinorIndex());

	double targetSpread = m_currentLegsPtr->GetBind().GetOpenSpread();
	double rangeSpread = m_argument.GetOverTick() * majorMarket.GetVariety().GetUnitPrice();
	if (IsLimitBullOpen())
	{
		double currentSpread = CalculateCurrentLimitSpread(false, true);
		if (DoubleComparer::less(currentSpread+rangeSpread, targetSpread))
		{
			Log::Info(FT("Business[%d] LimitBull out of the open range: CurrentSpread[%.2f]+Range[%.2f] < TargetSpread[%.2f] MajorMarket[%.2f %.2f] MinorMarket[%.2f %.2f]"
				,GetId(),currentSpread,rangeSpread,targetSpread,majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
			return false;
		}
	}
	if (IsLimitBearOpen())
	{
		double currentSpread = CalculateCurrentLimitSpread(true, true);
		if (DoubleComparer::greater(currentSpread-rangeSpread, targetSpread))
		{
			Log::Info(FT("Business[%d] LimitBear out of the open range: CurrentSpread[%.2f]-Range[%.2f] > TargetSpread[%.2f] MajorMarket[%.2f %.2f] MinorMarket[%.2f %.2f]"
				,GetId(),currentSpread,rangeSpread,targetSpread,majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
			return false;
		}
	}
	return true;
}
bool SpreadBusiness::IsEnterTheOffsetRange() const
{
	Market const& majorMarket = m_marketSnapshot.GetMarket(GetMajorIndex());
	Market const& minorMarket = m_marketSnapshot.GetMarket(GetMinorIndex());
	if (majorMarket.IsContractValid())
	{
		if (DoubleComparer::less_equal(majorMarket.GetLastPrice(),0.00) || DoubleComparer::greater_equal(majorMarket.GetLastPrice(),DBL_MAX))
		{
			Log::Warn(FT("Business[%d] Get invalid majorMarket[%s:%.2f]", GetId(), GetMajorCode(), majorMarket.GetLastPrice()));
			return false;	
		}

		double rangeSpread = majorMarket.GetVariety().GetUnitPrice();
		double targetSpread = m_currentLegsPtr->GetBind().GetOffsetSpread();
		if (IsLimitBull())
		{
			double currentSpread = CalculateCurrentLimitSpread(false, false);
			if (DoubleComparer::less_equal(currentSpread-rangeSpread, targetSpread))
			{
				Log::Info(FT("Business[%d] Bullish LimitOffset Enter the offset range: CurrentSpread[%.2f]-Range[%.2f] <= TargetSpread[%.2f] MajorMarket[%.2f %.2f] MinorMarket[%.2f %.2f]"
					,GetId(),currentSpread,rangeSpread,targetSpread,majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
				return true;
			}
		}
		if (IsLimitBear())
		{
			double currentSpread = CalculateCurrentLimitSpread(true, false);
			if (DoubleComparer::greater_equal(currentSpread+rangeSpread, targetSpread))
			{
				Log::Info(FT("Business[%d] Bearish LimitOffset Enter the offset range: CurrentSpread[%.2f]+Range[%.2f] >= TargetSpread[%.2f] MajorMarket[%.2f %.2f] MinorMarket[%.2f %.2f]"
					,GetId(),currentSpread,rangeSpread,targetSpread,majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
				return true;
			}
		}
	}
	else
	{
		Log::Error(FT("Business[%d] Get invalid contractIndex[%d]", GetId(), GetMajorIndex()));
		return false;
	}
	return false;
}
bool SpreadBusiness::WithinTheOffsetRange() const
{
	Market const& majorMarket = m_marketSnapshot.GetMarket(GetMajorIndex());
	Market const& minorMarket = m_marketSnapshot.GetMarket(GetMinorIndex());

	double targetSpread = m_currentLegsPtr->GetBind().GetOffsetSpread();
	double rangeSpread = m_argument.GetOverTick() * majorMarket.GetVariety().GetUnitPrice();
	if (IsLimitBull())
	{
		double currentSpread = CalculateCurrentLimitSpread(false, false);
		if (DoubleComparer::greater(currentSpread-rangeSpread, targetSpread))
		{
			Log::Info(FT("Business[%d] LimitBull out of the offset range: CurrentSpread[%.2f]-Range[%.2f] > TargetSpread[%.2f] MajorMarket[%.2f %.2f] MinorMarket[%.2f %.2f]"
				,GetId(),currentSpread,rangeSpread,targetSpread,majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
			return false;
		}
	}
	if (IsLimitBear())
	{
		double currentSpread = CalculateCurrentLimitSpread(true, false);
		if (DoubleComparer::less(currentSpread+rangeSpread, targetSpread))
		{
			Log::Info(FT("Business[%d] LimitBear out of the offset range: CurrentSpread[%.2f]+Range[%.2f] < TargetSpread[%.2f] MajorMarket[%.2f %.2f] MinorMarket[%.2f %.2f]"
				,GetId(),currentSpread,rangeSpread,targetSpread,majorMarket.GetSell1().GetPrice(),majorMarket.GetBuy1().GetPrice(),minorMarket.GetSell1().GetPrice(),minorMarket.GetBuy1().GetPrice()));
			return false;
		}
	}
	return true;
}
bool SpreadBusiness::WithinThePriceRange(bool isBuy, double orderPrice, int overflowTick) const
{
	Market const& minorMarket = m_marketSnapshot.GetMarket(GetMinorIndex());
	double sellPrice = minorMarket.GetSell1().GetPrice();
	double buyPrice = minorMarket.GetBuy1().GetPrice();
	double priceRange = overflowTick * minorMarket.GetVariety().GetUnitPrice();
	if (isBuy==true && DoubleComparer::greater(buyPrice, orderPrice+priceRange))
	{
		Log::Debug(FT("Business[%d] BuyPrice is out of the range: minorBuyPrice[%.2f] > orderPrice[%.2f]+priceRange[%.2f]",GetId(),buyPrice,orderPrice,priceRange));
		return true;
	}

	if (isBuy==false && DoubleComparer::less(sellPrice, orderPrice-priceRange))
	{
		Log::Debug(FT("Business[%d] SellPrice is out of the range: minorSellPrice[%.2f] < orderPrice[%.2f]-priceRange[%.2f]",GetId(),sellPrice,orderPrice,priceRange));
		return true;
	}
	return false;
}
double SpreadBusiness::CalculateMinorOrderPrice(Leg const& leg, bool isBearish) const
{
	Market const& majorMarket = m_marketSnapshot.GetMarket(GetMajorIndex());
	if (isBearish)
	{
		if (leg.IsOpen())
		{
			double targetSpread = m_argument.GetBearishBind().GetOpenSpread();
			return m_argument.GetContracts().IsMajorEqualFarther() ? (majorMarket.GetSell1().GetPrice()-targetSpread) : (majorMarket.GetBuy1().GetPrice()+targetSpread);
		}
		else
		{
			double offsetSpread = m_argument.GetBearishBind().GetOffsetSpread();
			return m_argument.GetContracts().IsMajorEqualFarther() ? (majorMarket.GetBuy1().GetPrice()-offsetSpread) : (majorMarket.GetSell1().GetPrice()+offsetSpread);
		}
	}
	else
	{
		if (leg.IsOpen())
		{
			double targetSpread = m_argument.GetBullishBind().GetOpenSpread();
			return m_argument.GetContracts().IsMajorEqualFarther() ? (majorMarket.GetBuy1().GetPrice()-targetSpread) : (majorMarket.GetSell1().GetPrice()+targetSpread);
		}
		else
		{
			double offsetSpread = m_argument.GetBullishBind().GetOffsetSpread();
			return m_argument.GetContracts().IsMajorEqualFarther() ? (majorMarket.GetSell1().GetPrice()-offsetSpread) : (majorMarket.GetBuy1().GetPrice()+offsetSpread);
		}
	}
}

OrderInfo SpreadBusiness::CreateOrder(Leg const& leg, int quantity, double price, OrderCondition::TYPE orderType) const
{
	Market const& market = m_marketSnapshot.GetMarket(leg.GetContractIndex());
	Exchange::FLAG exchange = market.GetVariety().GetExchange();

	OrderInfo orderInfo = {0};
	orderInfo.businessId = GetId();
	orderInfo.businessPtr = (Business*)(this);
	strcpy_s(orderInfo.clientId, GetUser().GetClientId(exchange));
	strcpy_s(orderInfo.contractCode, market.GetVariety().GetContractCode());
	orderInfo.exchange = exchange;
	orderInfo.contractIndex = leg.GetContractIndex();	
	orderInfo.direction = leg.GetDirection();
	if (SpreadArgument::OFFSET == GetArgument().GetOperation())
	{
		orderInfo.operation = Operation::OFFSET;
	}
	else if (SpreadArgument::OFFSET_TODAY == GetArgument().GetOperation())
	{
		orderInfo.operation = Operation::OFFSET_TODAY;
	}
	else
	{
		orderInfo.operation = leg.GetOperation();
	}
	orderInfo.hedgeFlag = leg.GetHedgeFlag();
	orderInfo.quantity = quantity;
	orderInfo.price = price;
	orderInfo.orderCondition = orderType;
	orderInfo.localId = LocalIdACC::Generate();
	
	return orderInfo;
}
Dealing& SpreadBusiness::PushOrder(OrderInfo const& orderInfo, Exchange::FLAG connectionExchange)
{
	Dealing dealing(m_user, orderInfo);
	FundsService::FreezeDealingFunds(m_marketSnapshot, dealing);

	ChipMessage message = {0};
	message.type = ChipMessage::ORDER_INSERT;
	memcpy_s(message.data,sizeof(message.data),&dealing,sizeof(Dealing));
	m_vectorRegister.GetExternalQueue(connectionExchange).Push(message);
	return m_dealingCache.Save(dealing);
}
OrderAction& SpreadBusiness::PushAction(OrderAction const& action, Exchange::FLAG connectionExchange)
{
	StartCancellationCounter();
	
	ChipMessage message = {0};
	message.type = ChipMessage::ORDER_ACTION;
	memcpy_s(message.data,sizeof(message.data),&action,sizeof(action));
	m_vectorRegister.GetExternalQueue(connectionExchange).Push(message);
	return m_actionCache.Save(action);
}

bool SpreadBusiness::IsOperationForbidden()
{
	Market const& market = m_marketSnapshot.GetMarket(m_argument.GetContracts().GetMajor());
	if (Exchange::CFFEX == market.GetVariety().GetExchange())
	{
		return (15==market.GetTime().GetHour() && market.GetTime().GetMinute()>=14) || (market.GetTime().GetHour()<9 || market.GetTime().GetHour()<21);
	}
	return (14==market.GetTime().GetHour() && market.GetTime().GetMinute()>=59) || (market.GetTime().GetHour()<9);
}